package com.gopay.scheduler.service.rclisting.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.gopay.common.cipher.utils.SensitiveInfoUtils;
import com.gopay.common.domain.riskcontrol.RcRuleGrayCorpRel;
import com.gopay.common.order.dao.CommonQueryDAO;
import com.gopay.scheduler.dao.RcListingRemoveDuplicateDAO;
import com.gopay.scheduler.dao.RcRuleGrayCorpRelDAO;
import com.gopay.scheduler.service.rclisting.RcListingRemoveDuplicateService;

/**
 * @ClassName: RcListingRemoveDuplicateServiceImpl
 * @Description: 黑灰名单定时去重业务实现
 * @author liuyu
 * @date 2016年11月14日 下午2:12:19
 */

@Service("rcListingRemoveDuplicateService")
public class RcListingRemoveDuplicateServiceImpl implements RcListingRemoveDuplicateService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource(name = "rcListingRemoveDuplicateDAO")
    private RcListingRemoveDuplicateDAO rcListingRemoveDuplicateDAO;

    @Resource(name = "commonQueryDAO")
    private CommonQueryDAO commonQueryDAO;
    
    @Resource(name = "rcRuleGrayCorpRelDAO")
    private RcRuleGrayCorpRelDAO rcRuleGrayCorpRelDAO;

    /*
     * (非 Javadoc)
     * 
     * 
     * @see
     * com.gopay.scheduler.service.rclisting.RcListingRemoveDuplicateService
     * #removeDuplicate()
     */
    @Override
    @Transactional
    public void removeDuplicate() {
        List<String> elems = new ArrayList<>();// 验重分组关键字
        elems.add("CUST_ID");
        elems.add("EMAIL_MOB");
        elems.add("CERT_NO");
        elems.add("BUSINESS_LICENSE_NO");
        elems.add("ORGANIZATION_CODE");
        elems.add("TAXPAYER_CODE");
        elems.add("CREDIT_CODE");
        elems.add("CUST_NAME");
        for (String elem : elems) {// 遍历关键字
            List<Object[]> groups = groupByElem(elem);// 根据关键字分组查询
            if (null != groups && !groups.isEmpty()) {// 按分组遍历
                for (Object[] group : groups) {
                    if (null == group[0]) {//关键字值为null，跳出当次循环
                        continue;
                    }
                    String elemValue = (String) group[0];// 第一个字段是关键字名称
                    int count = ((BigDecimal) group[1]).intValue();//第二个字段是分组查到的记录数
                    if (null != elemValue && !"".equals(elemValue) && count > 1) {
                        Map<String, Object> params = new HashMap<>();

                        params.put(convertElem(elem), elemValue);//设定查询条件
                        List<Object[]> list = searchDuplicateRecords(params, elem);//根据关键字查询可能重复的记录
                        if (null != list && list.size() > 1) {
                            for (int i = 0; i < list.size();) {
                                int record = 0;//标识被删除的记录，0为没有删除，1为外层记录被删除，2为内层记录被删除
                                for (int j = i + 1; j < list.size();) {
                                    //循环比较，并返回被删除记录的标记
                                    record = compareOjbect(list.get(i),list.get(j),list);
                                    if(2 == record){
                                        //记录2被删除，list长度减1，结束当次循环，内层角标不变
                                        continue;
                                    }else if(1 == record){
                                        //记录1被删除，跳出内层循环
                                        break;
                                    }else {
                                        j++;
                                    }
                                }
                                if(1 == record){
                                    //list长度减1，结束当次循环，比外层角标不变，内层重新开启循环
                                    continue;
                                }
                                i++;//如果记录1没有被删除，当次循环结束前角标正常+1
                            }
                        }
                    }
                }
            }
        }
    }

    /*
     * 查询关键字转换方法
     */
    private String convertElem(String elem) {
        String[] strs = elem.toLowerCase().split("_");// 查询关键字转小写并根据下划线分割
        if (null == strs || 0 == strs.length) {
            return null;
        }
        StringBuilder elemKey = new StringBuilder(strs[0]);// 第一个单词不需要处理
        if (strs.length > 1) {
            for (int i = 1; i < strs.length; i++) {
                elemKey.append(StringUtils.capitalize(strs[i]));// 后面每个单词首字母变大写拼入
            }
        }
        return elemKey.toString();
    }

    /*
     * 根据关键字分组查询，返回分组后信息
     * elem：关键字段名称
     */
    private List<Object[]> groupByElem(String elem) {
        StringBuilder sql = new StringBuilder();
        sql.append(" select t.").append(elem).append(", count(*) c ");
        sql.append(" from RC_LISTING t ");
        sql.append(" where t.stat = '01' ");//查询名单生效的信息
        sql.append(" group by t.").append(elem);
        return commonQueryDAO.findBySql(sql.toString());
    }

    /*
     * 根据分组后查询到的参数查询有可能重复的信息
     * params:参数列表，key为关键字段名，value为关键字段值
     */
    private List<Object[]> searchDuplicateRecords(Map<String, Object> params, String elem) {
        StringBuilder sql = new StringBuilder();
        //拼接需要验重的关键字
        sql.append(" select t.ID, t.CUST_ID, t.CUST_TYPE, t.TYPE, ");
        sql.append("        t.ACCT_ID, t.EMAIL_MOB, t.CUST_NAME, ");
        sql.append("        t.BUSINESS_LICENSE_NO, t.ORGANIZATION_CODE, t.TAXPAYER_CODE, ");
        sql.append("        t.CREDIT_CODE, t.CERT_NO, t.SOURCE, t.IS_BIND_RULE ");
        sql.append(" from RC_LISTING t ");
        sql.append(" where t.STAT = '01' ");//查询有效名单
        Set<Entry<String,Object>> entrySet = params.entrySet();
        for (Entry<String, Object> entry : entrySet) {
            if (null != entry.getKey() && !"".equals(entry.getKey())) {
            	
   		       /** start 央行检测 修改手机号  modify by ssj 2017-04-24    手机号  身份证号 加密查询  考虑到后期可能会有多个秘钥体系采用in进行查询*/
            	if(elem.equals("EMAIL_MOB") || elem.equals("CERT_NO")||elem.equals("CUST_NAME")){
            	    sql.append(" and t.").append(elem).append(" in (:").append(entry.getKey()).append(")");
            	    params.put(entry.getKey(), SensitiveInfoUtils.getAllHisCipherText(entry.getValue().toString()).toArray(new String[0]));
            	    
            	}else{
            	/**end*/
            		  sql.append(" and t.").append(elem).append(" = :").append(entry.getKey());
            	}
              
            }
        }
        List<Object[]> list = commonQueryDAO.findBySql(sql.toString(), params);
        /**  start   2017/04/24 ssj 央行检测   改造 对持卡人姓名   身份证件号  手机号  进行解密处理 */
        if(list!=null && list.size()>0){
       	 for (int i = 0; i < list.size(); i++) {
       		 //号进行解密处理 
       		 Object[] obj=(Object[]) list.get(i);
       		 if(obj[5]!=null){
       			obj[5]=SensitiveInfoUtils.smartDecryptWithCheck(obj[5].toString());
       		 }
       		 if(obj[6]!=null){
       			obj[6]=SensitiveInfoUtils.smartDecryptWithCheck(obj[6].toString());
       		 }
       		 if(obj[11]!=null){
       			 obj[11]=SensitiveInfoUtils.smartDecryptWithCheck(obj[11].toString());
       		 }
			}
       	 
        }
        /**end*/
        return list;
    }

    /*
     * 比较两个对象是否重复
     * record1:对象1
     * record2:对象2
     * list:保存可能重复对象的集合，为了减少循环次数，对象去重后，要去掉list中的元素，防止重复遍历
     */
    @Transactional
    public int compareOjbect(Object[] record1, Object[] record2, List<Object[]> list) {
        Map<String, Object> param = new HashMap<>();
        int total = record1.length - 3;// 记录对比属性的总数, ID、来源、风控规则字段不进行比较
        int equalCount = 0;// 记录查重关键字相等的数量
        int lackO1Count = 0;// 记录record1中查重关键字为null的数量
        int lackO2Count = 0;// 记录record2中查重关键字为null的数量
        //第1个字段是ID，倒数第2个字段是来源，最后一个字段是风控规则，不作比较，比较的关键字段都是字符串
        for(int i = 1; i <= total; i++){
            String o1 = (String) record1[i];
            String o2 = (String) record2[i];
            if (null == o1 && null == o2) {// 记录1和记录2该属性均为null，属性重复+1
                equalCount++;
            } else if (null != o1 && null == o2) {
                // 记录2该属性为空，记录2属性空缺+1
                lackO2Count++;
            } else if (null == o1 && null != o2) {
                // 记录1该属性为空，记录1属性空缺+1
                lackO1Count++;
            } else if (o1.equals(o2)) {// 记录1和记录2属性相同，属性重复+1
                equalCount++;
            }
        }
        if (equalCount == total || equalCount + lackO1Count == total) {
            // 两条记录关键字相同或者记录2完全包含记录1，删除记录1
            if(null != record1[record1.length - 1] 
                    && "1".equals(record1[record1.length - 1])){
                //要删除记录1前，如果记录1的规则绑定状态为1，说明绑定了规则，需要合并
                ruleMerge(record1, record2);
            } 
            param.put("id", ((BigDecimal)record1[0]).longValue());
            String hql = "update RcListing t set t.stat = '02' where id = :id";
            rcListingRemoveDuplicateDAO.updateByHql(hql, param);
            if("反诈骗平台".equals(record1[record1.length - 2])){
                String updateSource = "update RcListing t set t.source = '反诈骗平台' where id = '" + (BigDecimal)record2[0] + "'";
                rcListingRemoveDuplicateDAO.updateByHql(updateSource); 
            }
            list.remove(record1);
            logger.error("去掉重复记录，record-id: " + record1[0] + ", 参照record-id:" + record2[0]);
            return 1;
        } else if (equalCount + lackO2Count == total) {
            // 记录1完全包含记录2，删除记录2
            if(null != record2[record2.length - 1] 
                    && "1".equals(record2[record2.length - 1])){
                ruleMerge(record2, record1);
            } 
            param.put("id", ((BigDecimal)record2[0]).longValue());
            String hql = "update RcListing t set t.stat = '02' where id = :id";
            rcListingRemoveDuplicateDAO.updateByHql(hql, param);
            if("反诈骗平台".equals(record2[record2.length - 2])){
                String updateSource = "update RcListing t set t.source = '反诈骗平台' where id = '" + (BigDecimal)record1[0] + "'";
                rcListingRemoveDuplicateDAO.updateByHql(updateSource); 
            }
            list.remove(record2);
            logger.error("去掉重复记录，record-id: " + record2[0] + ", 参照record-id:" + record1[0] );
            return 2;
        }
        // 其他情况，两个记录有字段不同，同时保留
        return 0;
    }
    
    /*
     * 合并绑定规则的方法
     * deleted:准备删除的黑灰名单对象
     * merge:需要保留的黑灰名单对象
     */
    @Transactional
    public void ruleMerge(Object[] deleted, Object[] merge){
        //设置根据listId查询参数
        Map<String,Object> paramDeleted = new HashMap<>();
        paramDeleted.put("listId", ((BigDecimal)deleted[0]).longValue());
        Map<String,Object> paramMerge = new HashMap<>();
        paramMerge.put("listId", ((BigDecimal)merge[0]).longValue());
        
        String sql1 = "select * from rc_rule_gray_corp_rel t where t.list_id = :listId";
        //查询对象1绑定的规则
        List<Object[]> bindRules1 = commonQueryDAO.findBySql(sql1, paramDeleted);
        String sql2 = "select * from rc_rule_gray_corp_rel t where t.list_id = :listId and t.code = :code";
        //只有绑定了规则才会进到这个方法，因此不需验空
        for (int i = 0; i < bindRules1.size(); i++) {
            //遍历获取对象1的每一个绑定规则，并设置为对象2的查询参数
            paramMerge.put("code", (String)bindRules1.get(i)[3]);
            //根据对象2的listId和对象1的绑定规则查询记录
            List<Object[]> bindRules2 = commonQueryDAO.findBySql(sql2, paramMerge);
            
            if(null == bindRules2 || bindRules2.isEmpty()){
                //如果在对象2没有查到相应记录，则新增对象2规则
                RcRuleGrayCorpRel entity = new RcRuleGrayCorpRel();
                entity.setListId((BigDecimal)(merge[0]));
                entity.setCustId((String)(merge[1]));
                entity.setCode((String)(bindRules1.get(i)[3]));
                rcRuleGrayCorpRelDAO.save(entity);
            }
            //不管对象2有没有查到记录，对象1的规则都需要删除
            String delHql = "delete from RcRuleGrayCorpRel where id = '"
                    + ((BigDecimal)(bindRules1.get(i)[0])).longValue() + "'";
            rcRuleGrayCorpRelDAO.deleteByHql(delHql);
        }
    }
}
